2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/adaptor-framework/key.h>
26 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
27 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
33 #if defined(DEBUG_ENABLED)
34 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
45 bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl)
47 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n");
49 EventData*& eventData = impl.mEventData;
52 // Nothing to do if there is no text input.
53 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n");
57 unsigned int oldPos = eventData->mPrimaryCursorPosition;
59 if(eventData->mDecorator)
61 for(std::vector<Event>::iterator iter = eventData->mEventQueue.begin();
62 iter != eventData->mEventQueue.end();
67 case Event::CURSOR_KEY_EVENT:
69 OnCursorKeyEvent(impl, *iter);
72 case Event::TAP_EVENT:
74 OnTapEvent(impl, *iter);
77 case Event::LONG_PRESS_EVENT:
79 OnLongPressEvent(impl, *iter);
82 case Event::PAN_EVENT:
84 OnPanEvent(impl, *iter);
87 case Event::GRAB_HANDLE_EVENT:
88 case Event::LEFT_SELECTION_HANDLE_EVENT:
89 case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through
91 OnHandleEvent(impl, *iter);
96 OnSelectEvent(impl, *iter);
99 case Event::SELECT_ALL:
101 OnSelectAllEvent(impl);
104 case Event::SELECT_NONE:
106 OnSelectNoneEvent(impl);
109 case Event::SELECT_RANGE:
111 OnSelectRangeEvent(impl, *iter);
118 if(eventData->mUpdateCursorPosition ||
119 eventData->mUpdateHighlightBox)
121 impl.NotifyInputMethodContext();
124 // The cursor must also be repositioned after inserts into the model
125 if(eventData->mUpdateCursorPosition)
127 // Updates the cursor position and scrolls the text to make it visible.
128 CursorInfo cursorInfo;
130 // Calculate the cursor position from the new cursor index.
131 impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo);
133 //only emit the event if the cursor is moved in current function.
134 if(nullptr != impl.mEditableControlInterface && eventData->mEventQueue.size() > 0)
136 impl.mEditableControlInterface->CursorPositionChanged(oldPos, eventData->mPrimaryCursorPosition);
139 if(eventData->mUpdateCursorHookPosition)
141 // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'.
142 eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x;
143 eventData->mUpdateCursorHookPosition = false;
146 // Scroll first the text after delete ...
147 if(eventData->mScrollAfterDelete)
149 impl.ScrollTextToMatchCursor(cursorInfo);
152 // ... then, text can be scrolled to make the cursor visible.
153 if(eventData->mScrollAfterUpdatePosition)
155 const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset);
156 impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight);
158 eventData->mScrollAfterUpdatePosition = false;
159 eventData->mScrollAfterDelete = false;
161 impl.UpdateCursorPosition(cursorInfo);
163 eventData->mDecoratorUpdated = true;
164 eventData->mUpdateCursorPosition = false;
165 eventData->mUpdateGrabHandlePosition = false;
168 if(eventData->mUpdateHighlightBox ||
169 eventData->mUpdateLeftSelectionPosition ||
170 eventData->mUpdateRightSelectionPosition)
172 CursorInfo leftHandleInfo;
173 CursorInfo rightHandleInfo;
175 if(eventData->mUpdateHighlightBox)
177 impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo);
179 impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo);
181 if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition))
183 if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected)
185 CursorInfo& infoLeft = leftHandleInfo;
187 const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset);
188 impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight);
190 CursorInfo& infoRight = rightHandleInfo;
192 const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset);
193 impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight);
197 CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo;
199 const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset);
200 impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight);
205 if(eventData->mUpdateLeftSelectionPosition)
207 impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo);
209 impl.SetPopupButtons();
210 eventData->mDecoratorUpdated = true;
211 eventData->mUpdateLeftSelectionPosition = false;
214 if(eventData->mUpdateRightSelectionPosition)
216 impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo);
218 impl.SetPopupButtons();
219 eventData->mDecoratorUpdated = true;
220 eventData->mUpdateRightSelectionPosition = false;
223 if(eventData->mUpdateHighlightBox)
225 impl.RepositionSelectionHandles();
227 eventData->mUpdateLeftSelectionPosition = false;
228 eventData->mUpdateRightSelectionPosition = false;
229 eventData->mUpdateHighlightBox = false;
230 eventData->mIsLeftHandleSelected = false;
231 eventData->mIsRightHandleSelected = false;
234 eventData->mScrollAfterUpdatePosition = false;
237 if(eventData->mUpdateInputStyle)
239 // Keep a copy of the current input style.
240 InputStyle currentInputStyle;
241 currentInputStyle.Copy(eventData->mInputStyle);
243 // Set the default style first.
244 impl.RetrieveDefaultInputStyle(eventData->mInputStyle);
246 // Get the character index from the cursor index.
247 const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u;
249 // Retrieve the style from the style runs stored in the logical model.
250 impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle);
252 // Compare if the input style has changed.
253 const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle);
255 if(hasInputStyleChanged)
257 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle);
258 // Queue the input style changed signal.
259 eventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
262 eventData->mUpdateInputStyle = false;
265 eventData->mEventQueue.clear();
267 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n");
269 const bool decoratorUpdated = eventData->mDecoratorUpdated;
270 eventData->mDecoratorUpdated = false;
272 return decoratorUpdated;
275 void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event)
277 if(NULL == impl.mEventData || !impl.IsShowingRealText())
279 // Nothing to do if there is no text input.
283 int keyCode = event.p1.mInt;
284 bool isShiftModifier = event.p2.mBool;
285 EventData& eventData = *impl.mEventData;
286 ModelPtr& model = impl.mModel;
287 LogicalModelPtr& logicalModel = model->mLogicalModel;
288 VisualModelPtr& visualModel = model->mVisualModel;
290 CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition;
291 CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition;
293 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode)
295 if(primaryCursorPosition > 0u)
297 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
299 primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
303 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u);
307 else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
309 if(logicalModel->mText.Count() > primaryCursorPosition)
311 if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible())
313 primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
317 primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition);
321 else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier)
323 // Ignore Shift-Up for text selection for now.
325 // Get first the line index of the current cursor position index.
326 CharacterIndex characterIndex = 0u;
328 if(primaryCursorPosition > 0u)
330 characterIndex = primaryCursorPosition - 1u;
333 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
334 const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex);
336 // Retrieve the cursor position info.
337 CursorInfo cursorInfo;
338 impl.GetCursorPosition(primaryCursorPosition,
341 // Get the line above.
342 const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex);
344 // Get the next hit 'y' point.
345 const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line);
347 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
348 bool matchedCharacter = false;
349 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
352 eventData.mCursorHookPositionX,
354 CharacterHitTest::TAP,
357 else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier)
359 // Ignore Shift-Down for text selection for now.
361 // Get first the line index of the current cursor position index.
362 CharacterIndex characterIndex = 0u;
364 if(primaryCursorPosition > 0u)
366 characterIndex = primaryCursorPosition - 1u;
369 const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex);
371 if(lineIndex + 1u < visualModel->mLines.Count())
373 // Retrieve the cursor position info.
374 CursorInfo cursorInfo;
375 impl.GetCursorPosition(primaryCursorPosition, cursorInfo);
377 // Get the line below.
378 const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u);
380 // Get the next hit 'y' point.
381 const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line);
383 // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index.
384 bool matchedCharacter = false;
385 primaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
388 eventData.mCursorHookPositionX,
390 CharacterHitTest::TAP,
395 if(!isShiftModifier && eventData.mState != EventData::SELECTING)
397 // Update selection position after moving the cursor
398 eventData.mLeftSelectionPosition = primaryCursorPosition;
399 eventData.mRightSelectionPosition = primaryCursorPosition;
402 if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag)
404 // Handle text selection
405 bool selecting = false;
407 if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode)
409 // Shift-Left/Right to select the text
410 int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition;
411 if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary
413 uint32_t oldStart = eventData.mLeftSelectionPosition;
414 uint32_t oldEnd = eventData.mRightSelectionPosition;
416 eventData.mRightSelectionPosition += cursorPositionDelta;
418 if(impl.mSelectableControlInterface != nullptr)
420 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
425 else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition)
427 // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text
433 // Notify the cursor position to the InputMethodContext.
434 if(eventData.mInputMethodContext)
436 eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition);
437 eventData.mInputMethodContext.NotifyCursorPosition();
440 impl.ChangeState(EventData::SELECTING);
442 eventData.mUpdateLeftSelectionPosition = true;
443 eventData.mUpdateRightSelectionPosition = true;
444 eventData.mUpdateGrabHandlePosition = true;
445 eventData.mUpdateHighlightBox = true;
447 // Hide the text selection popup if select the text using keyboard instead of moving grab handles
448 if(eventData.mGrabHandlePopupEnabled)
450 eventData.mDecorator->SetPopupActive(false);
456 // Handle normal cursor move
457 impl.ChangeState(EventData::EDITING);
458 eventData.mUpdateCursorPosition = true;
461 eventData.mUpdateInputStyle = true;
462 eventData.mScrollAfterUpdatePosition = true;
465 void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event)
469 const unsigned int tapCount = event.p1.mUint;
470 EventData& eventData = *impl.mEventData;
471 ModelPtr& model = impl.mModel;
472 LogicalModelPtr& logicalModel = model->mLogicalModel;
473 VisualModelPtr& visualModel = model->mVisualModel;
477 if(impl.IsShowingRealText())
479 // Convert from control's coords to text's coords.
480 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
481 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
483 // Keep the tap 'x' position. Used to move the cursor.
484 eventData.mCursorHookPositionX = xPosition;
486 // Whether to touch point hits on a glyph.
487 bool matchedCharacter = false;
488 eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel,
493 CharacterHitTest::TAP,
496 // When the cursor position is changing, delay cursor blinking
497 eventData.mDecorator->DelayCursorBlink();
501 eventData.mPrimaryCursorPosition = 0u;
504 // Update selection position after tapping
505 eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition;
506 eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
508 eventData.mUpdateCursorPosition = true;
509 eventData.mUpdateGrabHandlePosition = true;
510 eventData.mScrollAfterUpdatePosition = true;
511 eventData.mUpdateInputStyle = true;
513 // Notify the cursor position to the InputMethodContext.
514 if(eventData.mInputMethodContext)
516 eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition);
517 eventData.mInputMethodContext.NotifyCursorPosition();
520 else if(2u == tapCount)
522 if(eventData.mSelectionEnabled)
524 // Convert from control's coords to text's coords.
525 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
526 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
528 // Calculates the logical position from the x,y coords.
529 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction);
535 void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event)
539 EventData& eventData = *impl.mEventData;
540 DecoratorPtr& decorator = eventData.mDecorator;
542 const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled();
543 const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled();
545 if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled)
547 // Nothing to do if scrolling is not enabled.
551 const GestureState state = static_cast<GestureState>(event.p1.mInt);
554 case GestureState::STARTED:
556 // Will remove the cursor, handles or text's popup, ...
557 impl.ChangeState(EventData::TEXT_PANNING);
560 case GestureState::CONTINUING:
562 ModelPtr& model = impl.mModel;
564 const Vector2& layoutSize = model->mVisualModel->GetLayoutSize();
565 Vector2& scrollPosition = model->mScrollPosition;
566 const Vector2 currentScroll = scrollPosition;
568 if(isHorizontalScrollEnabled)
570 const float displacementX = event.p2.mFloat;
571 scrollPosition.x += displacementX;
573 impl.ClampHorizontalScroll(layoutSize);
576 if(isVerticalScrollEnabled)
578 const float displacementY = event.p3.mFloat;
579 scrollPosition.y += displacementY;
581 impl.ClampVerticalScroll(layoutSize);
584 decorator->UpdatePositions(scrollPosition - currentScroll);
587 case GestureState::FINISHED:
588 case GestureState::CANCELLED: // FALLTHROUGH
590 // Will go back to the previous state to show the cursor, handles, the text's popup, ...
591 impl.ChangeState(eventData.mPreviousState);
600 void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event)
602 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n");
606 EventData& eventData = *impl.mEventData;
608 if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState))
610 impl.ChangeState(EventData::EDITING_WITH_POPUP);
611 eventData.mDecoratorUpdated = true;
612 eventData.mUpdateInputStyle = true;
616 if(eventData.mSelectionEnabled)
618 ModelPtr& model = impl.mModel;
620 // Convert from control's coords to text's coords.
621 const float xPosition = event.p2.mFloat - model->mScrollPosition.x;
622 const float yPosition = event.p3.mFloat - model->mScrollPosition.y;
624 // Calculates the logical position from the x,y coords.
625 impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction);
631 void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event)
635 const unsigned int state = event.p1.mUint;
636 const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state);
637 const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled();
639 if(HANDLE_PRESSED == state)
641 OnHandlePressed(impl, event, isSmoothHandlePanEnabled);
642 } // end ( HANDLE_PRESSED == state )
643 else if((HANDLE_RELEASED == state) ||
646 OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling);
647 } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
648 else if(HANDLE_SCROLLING == state)
650 OnHandleScrolling(impl, event, isSmoothHandlePanEnabled);
651 } // end ( HANDLE_SCROLLING == state )
655 void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event)
657 if(impl.mEventData && impl.mEventData->mSelectionEnabled)
659 ModelPtr& model = impl.mModel;
660 const Vector2& scrollPosition = model->mScrollPosition;
662 // Convert from control's coords to text's coords.
663 const float xPosition = event.p2.mFloat - scrollPosition.x;
664 const float yPosition = event.p3.mFloat - scrollPosition.y;
666 // Calculates the logical position from the x,y coords.
667 impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT);
671 void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl)
673 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
677 EventData& eventData = *impl.mEventData;
678 if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE)
680 ModelPtr& model = impl.mModel;
681 const Vector2& scrollPosition = model->mScrollPosition;
683 // Calculates the logical position from the start.
684 impl.RepositionSelectionHandles(0.f - scrollPosition.x,
685 0.f - scrollPosition.y,
686 Controller::NoTextTap::HIGHLIGHT);
688 uint32_t oldStart = eventData.mLeftSelectionPosition;
689 uint32_t oldEnd = eventData.mRightSelectionPosition;
691 eventData.mLeftSelectionPosition = 0u;
692 eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count();
694 if(impl.mSelectableControlInterface != nullptr)
696 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
702 void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl)
704 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false");
708 EventData& eventData = *impl.mEventData;
709 if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING)
711 eventData.mPrimaryCursorPosition = 0u;
712 uint32_t oldStart = eventData.mLeftSelectionPosition;
713 uint32_t oldEnd = eventData.mRightSelectionPosition;
715 eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition;
716 impl.ChangeState(EventData::INACTIVE);
717 eventData.mUpdateCursorPosition = true;
718 eventData.mUpdateInputStyle = true;
719 eventData.mScrollAfterUpdatePosition = true;
721 if(impl.mSelectableControlInterface != nullptr)
723 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
729 void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event)
731 if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE)
733 ModelPtr& model = impl.mModel;
734 const Vector2& scrollPosition = model->mScrollPosition;
736 // Calculate the selection index.
737 const uint32_t length = static_cast<uint32_t>(model->mLogicalModel->mText.Count());
738 const uint32_t start = std::min(event.p2.mUint, length);
739 const uint32_t end = std::min(event.p3.mUint, length);
743 uint32_t oldStart = impl.mEventData->mLeftSelectionPosition;
744 uint32_t oldEnd = impl.mEventData->mRightSelectionPosition;
746 // Calculates the logical position from the x,y coords.
747 impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT);
749 impl.mEventData->mLeftSelectionPosition = start;
750 impl.mEventData->mRightSelectionPosition = end;
752 if(impl.mSelectableControlInterface != nullptr)
754 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
760 void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
762 ModelPtr& model = impl.mModel;
763 const Vector2& scrollPosition = model->mScrollPosition;
765 // Convert from decorator's coords to text's coords.
766 const float xPosition = event.p2.mFloat - scrollPosition.x;
767 const float yPosition = event.p3.mFloat - scrollPosition.y;
769 // Need to calculate the handle's new position.
770 bool matchedCharacter = false;
771 const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel,
772 model->mLogicalModel,
776 CharacterHitTest::SCROLL,
779 EventData& eventData = *impl.mEventData;
780 uint32_t oldStart = eventData.mLeftSelectionPosition;
781 uint32_t oldEnd = eventData.mRightSelectionPosition;
783 if(Event::GRAB_HANDLE_EVENT == event.type)
785 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
787 if(handleNewPosition != eventData.mPrimaryCursorPosition)
789 // Updates the cursor position if the handle's new position is different than the current one.
790 eventData.mUpdateCursorPosition = true;
791 // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth).
792 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
793 eventData.mPrimaryCursorPosition = handleNewPosition;
796 // 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.
797 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
799 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
801 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
803 if((handleNewPosition != eventData.mLeftSelectionPosition) &&
804 (handleNewPosition != eventData.mRightSelectionPosition))
806 // Updates the highlight box if the handle's new position is different than the current one.
807 eventData.mUpdateHighlightBox = true;
808 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
809 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
810 eventData.mLeftSelectionPosition = handleNewPosition;
813 // 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.
814 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
816 // Will define the order to scroll the text to match the handle position.
817 eventData.mIsLeftHandleSelected = true;
818 eventData.mIsRightHandleSelected = false;
820 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
822 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
824 if((handleNewPosition != eventData.mRightSelectionPosition) &&
825 (handleNewPosition != eventData.mLeftSelectionPosition))
827 // Updates the highlight box if the handle's new position is different than the current one.
828 eventData.mUpdateHighlightBox = true;
829 // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth).
830 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
831 eventData.mRightSelectionPosition = handleNewPosition;
834 // 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.
835 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
837 // Will define the order to scroll the text to match the handle position.
838 eventData.mIsLeftHandleSelected = false;
839 eventData.mIsRightHandleSelected = true;
842 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
844 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
848 void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling)
850 CharacterIndex handlePosition = 0u;
851 if(handleStopScrolling || isSmoothHandlePanEnabled)
853 ModelPtr& model = impl.mModel;
854 const Vector2& scrollPosition = model->mScrollPosition;
856 // Convert from decorator's coords to text's coords.
857 const float xPosition = event.p2.mFloat - scrollPosition.x;
858 const float yPosition = event.p3.mFloat - scrollPosition.y;
860 bool matchedCharacter = false;
861 handlePosition = Text::GetClosestCursorIndex(model->mVisualModel,
862 model->mLogicalModel,
866 CharacterHitTest::SCROLL,
870 EventData& eventData = *impl.mEventData;
871 uint32_t oldStart = eventData.mLeftSelectionPosition;
872 uint32_t oldEnd = eventData.mRightSelectionPosition;
874 if(Event::GRAB_HANDLE_EVENT == event.type)
876 eventData.mUpdateCursorPosition = true;
877 eventData.mUpdateGrabHandlePosition = true;
878 eventData.mUpdateInputStyle = true;
880 if(!impl.IsClipboardEmpty())
882 impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup
885 if(handleStopScrolling || isSmoothHandlePanEnabled)
887 eventData.mScrollAfterUpdatePosition = true;
888 eventData.mPrimaryCursorPosition = handlePosition;
891 else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type)
893 impl.ChangeState(EventData::SELECTING);
895 eventData.mUpdateHighlightBox = true;
896 eventData.mUpdateLeftSelectionPosition = true;
897 eventData.mUpdateRightSelectionPosition = true;
899 if(handleStopScrolling || isSmoothHandlePanEnabled)
901 eventData.mScrollAfterUpdatePosition = true;
903 if((handlePosition != eventData.mRightSelectionPosition) &&
904 (handlePosition != eventData.mLeftSelectionPosition))
906 eventData.mLeftSelectionPosition = handlePosition;
910 else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type)
912 impl.ChangeState(EventData::SELECTING);
914 eventData.mUpdateHighlightBox = true;
915 eventData.mUpdateRightSelectionPosition = true;
916 eventData.mUpdateLeftSelectionPosition = true;
918 if(handleStopScrolling || isSmoothHandlePanEnabled)
920 eventData.mScrollAfterUpdatePosition = true;
921 if((handlePosition != eventData.mRightSelectionPosition) &&
922 (handlePosition != eventData.mLeftSelectionPosition))
924 eventData.mRightSelectionPosition = handlePosition;
929 if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition)))
931 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
934 eventData.mDecoratorUpdated = true;
937 void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled)
939 ModelPtr& model = impl.mModel;
940 Vector2& scrollPosition = model->mScrollPosition;
941 VisualModelPtr& visualModel = model->mVisualModel;
943 const float xSpeed = event.p2.mFloat;
944 const float ySpeed = event.p3.mFloat;
945 const Vector2& layoutSize = visualModel->GetLayoutSize();
946 const Vector2 currentScrollPosition = scrollPosition;
948 scrollPosition.x += xSpeed;
949 scrollPosition.y += ySpeed;
951 impl.ClampHorizontalScroll(layoutSize);
952 impl.ClampVerticalScroll(layoutSize);
954 EventData& eventData = *impl.mEventData;
955 DecoratorPtr& decorator = eventData.mDecorator;
957 bool endOfScroll = false;
958 if(Vector2::ZERO == (currentScrollPosition - scrollPosition))
960 // Notify the decorator there is no more text to scroll.
961 // The decorator won't send more scroll events.
962 decorator->NotifyEndOfScroll();
963 // Still need to set the position of the handle.
967 // Set the position of the handle.
968 const bool scrollRightDirection = xSpeed > 0.f;
969 const bool scrollBottomDirection = ySpeed > 0.f;
970 const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
971 const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
973 if(Event::GRAB_HANDLE_EVENT == event.type)
975 impl.ChangeState(EventData::GRAB_HANDLE_PANNING);
977 // Get the grab handle position in decorator coords.
978 Vector2 position = decorator->GetPosition(GRAB_HANDLE);
980 if(decorator->IsHorizontalScrollEnabled())
982 // Position the grag handle close to either the left or right edge.
983 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
986 if(decorator->IsVerticalScrollEnabled())
988 position.x = eventData.mCursorHookPositionX;
990 // Position the grag handle close to either the top or bottom edge.
991 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
994 // Get the new handle position.
995 // The grab handle's position is in decorator's coords. Need to transforms to text's coords.
996 bool matchedCharacter = false;
997 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
998 impl.mModel->mLogicalModel,
1000 position.x - scrollPosition.x,
1001 position.y - scrollPosition.y,
1002 CharacterHitTest::SCROLL,
1005 if(eventData.mPrimaryCursorPosition != handlePosition)
1007 eventData.mUpdateCursorPosition = true;
1008 eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled;
1009 eventData.mScrollAfterUpdatePosition = true;
1010 eventData.mPrimaryCursorPosition = handlePosition;
1012 eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition;
1014 // Updates the decorator if the soft handle panning is enabled.
1015 eventData.mDecoratorUpdated = isSmoothHandlePanEnabled;
1017 else if(leftSelectionHandleEvent || rightSelectionHandleEvent)
1019 impl.ChangeState(EventData::SELECTION_HANDLE_PANNING);
1021 // Get the selection handle position in decorator coords.
1022 Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE);
1024 if(decorator->IsHorizontalScrollEnabled())
1026 // Position the selection handle close to either the left or right edge.
1027 position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width;
1030 if(decorator->IsVerticalScrollEnabled())
1032 position.x = eventData.mCursorHookPositionX;
1034 // Position the grag handle close to either the top or bottom edge.
1035 position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height;
1038 // Get the new handle position.
1039 // The selection handle's position is in decorator's coords. Need to transform to text's coords.
1040 bool matchedCharacter = false;
1041 const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel,
1042 impl.mModel->mLogicalModel,
1044 position.x - scrollPosition.x,
1045 position.y - scrollPosition.y,
1046 CharacterHitTest::SCROLL,
1048 uint32_t oldStart = eventData.mLeftSelectionPosition;
1049 uint32_t oldEnd = eventData.mRightSelectionPosition;
1051 if(leftSelectionHandleEvent)
1053 const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition);
1055 if(differentHandles || endOfScroll)
1057 eventData.mUpdateHighlightBox = true;
1058 eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled;
1059 eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled;
1060 eventData.mLeftSelectionPosition = handlePosition;
1065 const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition);
1066 if(differentHandles || endOfScroll)
1068 eventData.mUpdateHighlightBox = true;
1069 eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled;
1070 eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled;
1071 eventData.mRightSelectionPosition = handlePosition;
1075 if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition)
1077 impl.RepositionSelectionHandles();
1079 eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled;
1081 if(impl.mSelectableControlInterface != nullptr)
1083 impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition);
1087 eventData.mDecoratorUpdated = true;
1092 } // namespace Toolkit