X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller-impl-event-handler.cpp;h=e298a5ad7ba0281ae1addac2442bb85e1cb49c65;hp=7b50a5c60a6a10c1b53fdf9e1996743540d926a2;hb=d44300a6f74767fb66b96d5583a60779a50c4c65;hpb=f8abce594a03f5c11e363da5c13f54000496008a diff --git a/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp b/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp index 7b50a5c..e298a5a 100644 --- a/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp +++ b/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,17 +19,17 @@ #include // EXTERNAL INCLUDES -#include #include +#include // INTERNAL INCLUDES #include +#include using namespace Dali; namespace { - #if defined(DEBUG_ENABLED) Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif @@ -38,225 +38,471 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT namespace Dali { - namespace Toolkit { - namespace Text { +bool ControllerImplEventHandler::ProcessInputEvents(Controller::Impl& impl) +{ + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n"); + + EventData*& eventData = impl.mEventData; + if(NULL == eventData) + { + // Nothing to do if there is no text input. + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n"); + return false; + } + + unsigned int oldPos = eventData->mPrimaryCursorPosition; + + if(eventData->mDecorator) + { + for(std::vector::iterator iter = eventData->mEventQueue.begin(); + iter != eventData->mEventQueue.end(); + ++iter) + { + switch(iter->type) + { + case Event::CURSOR_KEY_EVENT: + { + OnCursorKeyEvent(impl, *iter); + break; + } + case Event::TAP_EVENT: + { + OnTapEvent(impl, *iter); + break; + } + case Event::LONG_PRESS_EVENT: + { + OnLongPressEvent(impl, *iter); + break; + } + case Event::PAN_EVENT: + { + OnPanEvent(impl, *iter); + break; + } + case Event::GRAB_HANDLE_EVENT: + case Event::LEFT_SELECTION_HANDLE_EVENT: + case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through + { + OnHandleEvent(impl, *iter); + break; + } + case Event::SELECT: + { + OnSelectEvent(impl, *iter); + break; + } + case Event::SELECT_ALL: + { + OnSelectAllEvent(impl); + break; + } + case Event::SELECT_NONE: + { + OnSelectNoneEvent(impl); + break; + } + case Event::SELECT_RANGE: + { + OnSelectRangeEvent(impl, *iter); + break; + } + } + } + } + + if(eventData->mUpdateCursorPosition || + eventData->mUpdateHighlightBox) + { + impl.NotifyInputMethodContext(); + } + + // The cursor must also be repositioned after inserts into the model + if(eventData->mUpdateCursorPosition) + { + // Updates the cursor position and scrolls the text to make it visible. + CursorInfo cursorInfo; + + // Calculate the cursor position from the new cursor index. + impl.GetCursorPosition(eventData->mPrimaryCursorPosition, cursorInfo); + + //only emit the event if the cursor is moved in current function. + if(nullptr != impl.mEditableControlInterface && eventData->mEventQueue.size() > 0) + { + impl.mEditableControlInterface->CursorPositionChanged(oldPos, eventData->mPrimaryCursorPosition); + } + + if(eventData->mUpdateCursorHookPosition) + { + // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'. + eventData->mCursorHookPositionX = cursorInfo.primaryPosition.x; + eventData->mUpdateCursorHookPosition = false; + } + + // Scroll first the text after delete ... + if(eventData->mScrollAfterDelete) + { + impl.ScrollTextToMatchCursor(cursorInfo); + } + + // ... then, text can be scrolled to make the cursor visible. + if(eventData->mScrollAfterUpdatePosition) + { + const Vector2 currentCursorPosition(cursorInfo.primaryPosition.x, cursorInfo.lineOffset); + impl.ScrollToMakePositionVisible(currentCursorPosition, cursorInfo.lineHeight); + } + eventData->mScrollAfterUpdatePosition = false; + eventData->mScrollAfterDelete = false; + + impl.UpdateCursorPosition(cursorInfo); + + eventData->mDecoratorUpdated = true; + eventData->mUpdateCursorPosition = false; + eventData->mUpdateGrabHandlePosition = false; + } + + if(eventData->mUpdateHighlightBox || + eventData->mUpdateLeftSelectionPosition || + eventData->mUpdateRightSelectionPosition) + { + CursorInfo leftHandleInfo; + CursorInfo rightHandleInfo; + + if(eventData->mUpdateHighlightBox) + { + impl.GetCursorPosition(eventData->mLeftSelectionPosition, leftHandleInfo); + + impl.GetCursorPosition(eventData->mRightSelectionPosition, rightHandleInfo); + + if(eventData->mScrollAfterUpdatePosition && (eventData->mIsLeftHandleSelected ? eventData->mUpdateLeftSelectionPosition : eventData->mUpdateRightSelectionPosition)) + { + if(eventData->mIsLeftHandleSelected && eventData->mIsRightHandleSelected) + { + CursorInfo& infoLeft = leftHandleInfo; + + const Vector2 currentCursorPositionLeft(infoLeft.primaryPosition.x, infoLeft.lineOffset); + impl.ScrollToMakePositionVisible(currentCursorPositionLeft, infoLeft.lineHeight); + + CursorInfo& infoRight = rightHandleInfo; + + const Vector2 currentCursorPositionRight(infoRight.primaryPosition.x, infoRight.lineOffset); + impl.ScrollToMakePositionVisible(currentCursorPositionRight, infoRight.lineHeight); + } + else + { + CursorInfo& info = eventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo; + + const Vector2 currentCursorPosition(info.primaryPosition.x, info.lineOffset); + impl.ScrollToMakePositionVisible(currentCursorPosition, info.lineHeight); + } + } + } + + if(eventData->mUpdateLeftSelectionPosition) + { + impl.UpdateSelectionHandle(LEFT_SELECTION_HANDLE, leftHandleInfo); + + impl.SetPopupButtons(); + eventData->mDecoratorUpdated = true; + eventData->mUpdateLeftSelectionPosition = false; + } + + if(eventData->mUpdateRightSelectionPosition) + { + impl.UpdateSelectionHandle(RIGHT_SELECTION_HANDLE, rightHandleInfo); + + impl.SetPopupButtons(); + eventData->mDecoratorUpdated = true; + eventData->mUpdateRightSelectionPosition = false; + } + + if(eventData->mUpdateHighlightBox) + { + impl.RepositionSelectionHandles(); + + eventData->mUpdateLeftSelectionPosition = false; + eventData->mUpdateRightSelectionPosition = false; + eventData->mUpdateHighlightBox = false; + eventData->mIsLeftHandleSelected = false; + eventData->mIsRightHandleSelected = false; + } + + eventData->mScrollAfterUpdatePosition = false; + } + + if(eventData->mUpdateInputStyle) + { + // Keep a copy of the current input style. + InputStyle currentInputStyle; + currentInputStyle.Copy(eventData->mInputStyle); + + // Set the default style first. + impl.RetrieveDefaultInputStyle(eventData->mInputStyle); + + // Get the character index from the cursor index. + const CharacterIndex styleIndex = (eventData->mPrimaryCursorPosition > 0u) ? eventData->mPrimaryCursorPosition - 1u : 0u; + + // Retrieve the style from the style runs stored in the logical model. + impl.mModel->mLogicalModel->RetrieveStyle(styleIndex, eventData->mInputStyle); + + // Compare if the input style has changed. + const bool hasInputStyleChanged = !currentInputStyle.Equal(eventData->mInputStyle); + + if(hasInputStyleChanged) + { + const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(eventData->mInputStyle); + // Queue the input style changed signal. + eventData->mInputStyleChangedQueue.PushBack(styleChangedMask); + } + + eventData->mUpdateInputStyle = false; + } + + eventData->mEventQueue.clear(); + + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n"); + + const bool decoratorUpdated = eventData->mDecoratorUpdated; + eventData->mDecoratorUpdated = false; + + return decoratorUpdated; +} void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event) { - if( NULL == impl.mEventData || !impl.IsShowingRealText() ) + if(NULL == impl.mEventData || !impl.IsShowingRealText()) { // Nothing to do if there is no text input. return; } - int keyCode = event.p1.mInt; - bool isShiftModifier = event.p2.mBool; - EventData& eventData = *impl.mEventData; - ModelPtr& model = impl.mModel; - LogicalModelPtr& logicalModel = model->mLogicalModel; - VisualModelPtr& visualModel = model->mVisualModel; + int keyCode = event.p1.mInt; + bool isShiftModifier = event.p2.mBool; + EventData& eventData = *impl.mEventData; + ModelPtr& model = impl.mModel; + LogicalModelPtr& logicalModel = model->mLogicalModel; + VisualModelPtr& visualModel = model->mVisualModel; + uint32_t oldSelStart = eventData.mLeftSelectionPosition; + uint32_t oldSelEnd = eventData.mRightSelectionPosition; - CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition; - CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition; + CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition; + CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition; - if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) + if(Dali::DALI_KEY_CURSOR_LEFT == keyCode) { - if( primaryCursorPosition > 0u ) + if(primaryCursorPosition > 0u) { - if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() ) + if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible()) { primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); } else { - primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u ); + primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition - 1u); } } } - else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) + else if(Dali::DALI_KEY_CURSOR_RIGHT == keyCode) { - if( logicalModel->mText.Count() > primaryCursorPosition ) + if(logicalModel->mText.Count() > primaryCursorPosition) { - if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() ) + if(!isShiftModifier && eventData.mDecorator->IsHighlightVisible()) { primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); } else { - primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition ); + primaryCursorPosition = impl.CalculateNewCursorIndex(primaryCursorPosition); } } } - else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier ) + else if(Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier) { // Ignore Shift-Up for text selection for now. // Get first the line index of the current cursor position index. CharacterIndex characterIndex = 0u; - if( primaryCursorPosition > 0u ) + if(primaryCursorPosition > 0u) { characterIndex = primaryCursorPosition - 1u; } - const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex ); - const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); + const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex); + const LineIndex previousLineIndex = (lineIndex > 0 ? lineIndex - 1u : lineIndex); // Retrieve the cursor position info. CursorInfo cursorInfo; - impl.GetCursorPosition( primaryCursorPosition, - cursorInfo ); + impl.GetCursorPosition(primaryCursorPosition, + cursorInfo); // Get the line above. - const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex ); + const LineRun& line = *(visualModel->mLines.Begin() + previousLineIndex); // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); + const float hitPointY = cursorInfo.lineOffset - 0.5f * GetLineHeight(line); // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. bool matchedCharacter = false; - primaryCursorPosition = Text::GetClosestCursorIndex( visualModel, - logicalModel, - impl.mMetrics, - eventData.mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); + primaryCursorPosition = Text::GetClosestCursorIndex(visualModel, + logicalModel, + impl.mMetrics, + eventData.mCursorHookPositionX, + hitPointY, + CharacterHitTest::TAP, + matchedCharacter); } - else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier ) + else if(Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier) { // Ignore Shift-Down for text selection for now. // Get first the line index of the current cursor position index. CharacterIndex characterIndex = 0u; - if( primaryCursorPosition > 0u ) + if(primaryCursorPosition > 0u) { characterIndex = primaryCursorPosition - 1u; } - const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex ); + const LineIndex lineIndex = visualModel->GetLineOfCharacter(characterIndex); - if( lineIndex + 1u < visualModel->mLines.Count() ) + if(lineIndex + 1u < visualModel->mLines.Count()) { // Retrieve the cursor position info. CursorInfo cursorInfo; - impl.GetCursorPosition( primaryCursorPosition, cursorInfo ); + impl.GetCursorPosition(primaryCursorPosition, cursorInfo); // Get the line below. - const LineRun& line = *( visualModel->mLines.Begin() + lineIndex + 1u ); + const LineRun& line = *(visualModel->mLines.Begin() + lineIndex + 1u); // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender ); + const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * GetLineHeight(line); // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. bool matchedCharacter = false; - primaryCursorPosition = Text::GetClosestCursorIndex( visualModel, - logicalModel, - impl.mMetrics, - eventData.mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); + primaryCursorPosition = Text::GetClosestCursorIndex(visualModel, + logicalModel, + impl.mMetrics, + eventData.mCursorHookPositionX, + hitPointY, + CharacterHitTest::TAP, + matchedCharacter); } } - if ( !isShiftModifier && eventData.mState != EventData::SELECTING ) + if(!isShiftModifier && eventData.mState != EventData::SELECTING) { // Update selection position after moving the cursor - eventData.mLeftSelectionPosition = primaryCursorPosition; + eventData.mLeftSelectionPosition = primaryCursorPosition; eventData.mRightSelectionPosition = primaryCursorPosition; + + if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible()) + { + impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } } - if ( isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag ) + if(isShiftModifier && impl.IsShowingRealText() && eventData.mShiftSelectionFlag) { // Handle text selection bool selecting = false; - if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) + if(Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode) { // Shift-Left/Right to select the text int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition; - if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary + if(cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u) // Check the boundary { eventData.mRightSelectionPosition += cursorPositionDelta; + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } } selecting = true; } - else if ( eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition ) + else if(eventData.mLeftSelectionPosition != eventData.mRightSelectionPosition) { // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text selecting = true; } - if ( selecting ) + if(selecting) { // Notify the cursor position to the InputMethodContext. - if( eventData.mInputMethodContext ) + if(eventData.mInputMethodContext) { - eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition ); + eventData.mInputMethodContext.SetCursorPosition(primaryCursorPosition); eventData.mInputMethodContext.NotifyCursorPosition(); } - impl.ChangeState( EventData::SELECTING ); + impl.ChangeState(EventData::SELECTING); - eventData.mUpdateLeftSelectionPosition = true; + eventData.mUpdateLeftSelectionPosition = true; eventData.mUpdateRightSelectionPosition = true; - eventData.mUpdateGrabHandlePosition = true; - eventData.mUpdateHighlightBox = true; + eventData.mUpdateGrabHandlePosition = true; + eventData.mUpdateHighlightBox = true; // Hide the text selection popup if select the text using keyboard instead of moving grab handles - if( eventData.mGrabHandlePopupEnabled ) + if(eventData.mGrabHandlePopupEnabled) { - eventData.mDecorator->SetPopupActive( false ); + eventData.mDecorator->SetPopupActive(false); } } } else { // Handle normal cursor move - impl.ChangeState( EventData::EDITING ); + impl.ChangeState(EventData::EDITING); eventData.mUpdateCursorPosition = true; } - eventData.mUpdateInputStyle = true; + eventData.mUpdateInputStyle = true; eventData.mScrollAfterUpdatePosition = true; } void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event) { - if( impl.mEventData ) + if(impl.mEventData) { - const unsigned int tapCount = event.p1.mUint; - EventData& eventData = *impl.mEventData; - ModelPtr& model = impl.mModel; - LogicalModelPtr& logicalModel = model->mLogicalModel; - VisualModelPtr& visualModel = model->mVisualModel; + const unsigned int tapCount = event.p1.mUint; + EventData& eventData = *impl.mEventData; + ModelPtr& model = impl.mModel; + LogicalModelPtr& logicalModel = model->mLogicalModel; + VisualModelPtr& visualModel = model->mVisualModel; - if( 1u == tapCount ) + if(1u == tapCount) { - if( impl.IsShowingRealText() ) + if(impl.IsShowingRealText()) { // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - model->mScrollPosition.x; - const float yPosition = event.p3.mFloat - model->mScrollPosition.y; + const float xPosition = event.p2.mFloat - model->mScrollPosition.x; + const float yPosition = event.p3.mFloat - model->mScrollPosition.y; + uint32_t oldSelStart = eventData.mLeftSelectionPosition; + uint32_t oldSelEnd = eventData.mRightSelectionPosition; // Keep the tap 'x' position. Used to move the cursor. eventData.mCursorHookPositionX = xPosition; // Whether to touch point hits on a glyph. - bool matchedCharacter = false; - eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex( visualModel, - logicalModel, - impl.mMetrics, - xPosition, - yPosition, - CharacterHitTest::TAP, - matchedCharacter ); + bool matchedCharacter = false; + eventData.mPrimaryCursorPosition = Text::GetClosestCursorIndex(visualModel, + logicalModel, + impl.mMetrics, + xPosition, + yPosition, + CharacterHitTest::TAP, + matchedCharacter); + + if(impl.mSelectableControlInterface != nullptr && eventData.mDecorator->IsHighlightVisible()) + { + impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, eventData.mPrimaryCursorPosition, eventData.mPrimaryCursorPosition); + } // When the cursor position is changing, delay cursor blinking eventData.mDecorator->DelayCursorBlink(); @@ -267,31 +513,31 @@ void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& } // Update selection position after tapping - eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition; + eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition; eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition; - eventData.mUpdateCursorPosition = true; - eventData.mUpdateGrabHandlePosition = true; + eventData.mUpdateCursorPosition = true; + eventData.mUpdateGrabHandlePosition = true; eventData.mScrollAfterUpdatePosition = true; - eventData.mUpdateInputStyle = true; + eventData.mUpdateInputStyle = true; // Notify the cursor position to the InputMethodContext. - if( eventData.mInputMethodContext ) + if(eventData.mInputMethodContext) { - eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition ); + eventData.mInputMethodContext.SetCursorPosition(eventData.mPrimaryCursorPosition); eventData.mInputMethodContext.NotifyCursorPosition(); } } - else if( 2u == tapCount ) + else if(2u == tapCount) { - if( eventData.mSelectionEnabled ) + if(eventData.mSelectionEnabled) { // Convert from control's coords to text's coords. const float xPosition = event.p2.mFloat - model->mScrollPosition.x; const float yPosition = event.p3.mFloat - model->mScrollPosition.y; // Calculates the logical position from the x,y coords. - impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mDoubleTapAction ); + impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mDoubleTapAction); } } } @@ -299,61 +545,61 @@ void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event) { - if( impl.mEventData ) + if(impl.mEventData) { - EventData& eventData = *impl.mEventData; + EventData& eventData = *impl.mEventData; DecoratorPtr& decorator = eventData.mDecorator; const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled(); - const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled(); + const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled(); - if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled ) + if(!isHorizontalScrollEnabled && !isVerticalScrollEnabled) { // Nothing to do if scrolling is not enabled. return; } - const GestureState state = static_cast( event.p1.mInt ); - switch( state ) + const GestureState state = static_cast(event.p1.mInt); + switch(state) { case GestureState::STARTED: { // Will remove the cursor, handles or text's popup, ... - impl.ChangeState( EventData::TEXT_PANNING ); + impl.ChangeState(EventData::TEXT_PANNING); break; } case GestureState::CONTINUING: { ModelPtr& model = impl.mModel; - const Vector2& layoutSize = model->mVisualModel->GetLayoutSize(); - Vector2& scrollPosition = model->mScrollPosition; - const Vector2 currentScroll = scrollPosition; + const Vector2& layoutSize = model->mVisualModel->GetLayoutSize(); + Vector2& scrollPosition = model->mScrollPosition; + const Vector2 currentScroll = scrollPosition; - if( isHorizontalScrollEnabled ) + if(isHorizontalScrollEnabled) { const float displacementX = event.p2.mFloat; scrollPosition.x += displacementX; - impl.ClampHorizontalScroll( layoutSize ); + impl.ClampHorizontalScroll(layoutSize); } - if( isVerticalScrollEnabled ) + if(isVerticalScrollEnabled) { const float displacementY = event.p3.mFloat; scrollPosition.y += displacementY; - impl.ClampVerticalScroll( layoutSize ); + impl.ClampVerticalScroll(layoutSize); } - decorator->UpdatePositions( scrollPosition - currentScroll ); + decorator->UpdatePositions(scrollPosition - currentScroll); break; } case GestureState::FINISHED: case GestureState::CANCELLED: // FALLTHROUGH { // Will go back to the previous state to show the cursor, handles, the text's popup, ... - impl.ChangeState( eventData.mPreviousState ); + impl.ChangeState(eventData.mPreviousState); break; } default: @@ -364,21 +610,21 @@ void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event) { - DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::OnLongPressEvent\n"); - if( impl.mEventData ) + if(impl.mEventData) { EventData& eventData = *impl.mEventData; - if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) ) + if(!impl.IsShowingRealText() && (EventData::EDITING == eventData.mState)) { - impl.ChangeState( EventData::EDITING_WITH_POPUP ); + impl.ChangeState(EventData::EDITING_WITH_POPUP); eventData.mDecoratorUpdated = true; eventData.mUpdateInputStyle = true; } else { - if( eventData.mSelectionEnabled ) + if(eventData.mSelectionEnabled) { ModelPtr& model = impl.mModel; @@ -387,7 +633,7 @@ void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const const float yPosition = event.p3.mFloat - model->mScrollPosition.y; // Calculates the logical position from the x,y coords. - impl.RepositionSelectionHandles( xPosition, yPosition, eventData.mLongPressAction ); + impl.RepositionSelectionHandles(xPosition, yPosition, eventData.mLongPressAction); } } } @@ -395,33 +641,33 @@ void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event) { - if( impl.mEventData ) + if(impl.mEventData) { - const unsigned int state = event.p1.mUint; - const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state ); - const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled(); + const unsigned int state = event.p1.mUint; + const bool handleStopScrolling = (HANDLE_STOP_SCROLLING == state); + const bool isSmoothHandlePanEnabled = impl.mEventData->mDecorator->IsSmoothHandlePanEnabled(); - if( HANDLE_PRESSED == state ) + if(HANDLE_PRESSED == state) { OnHandlePressed(impl, event, isSmoothHandlePanEnabled); } // end ( HANDLE_PRESSED == state ) - else if( ( HANDLE_RELEASED == state ) || - handleStopScrolling ) + else if((HANDLE_RELEASED == state) || + handleStopScrolling) { OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling); } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) ) - else if( HANDLE_SCROLLING == state ) + else if(HANDLE_SCROLLING == state) { OnHandleScrolling(impl, event, isSmoothHandlePanEnabled); } // end ( HANDLE_SCROLLING == state ) } } -void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event ) +void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event) { - if( impl.mEventData && impl.mEventData->mSelectionEnabled ) + if(impl.mEventData && impl.mEventData->mSelectionEnabled) { - ModelPtr& model = impl.mModel; + ModelPtr& model = impl.mModel; const Vector2& scrollPosition = model->mScrollPosition; // Convert from control's coords to text's coords. @@ -429,55 +675,101 @@ void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Eve const float yPosition = event.p3.mFloat - scrollPosition.y; // Calculates the logical position from the x,y coords. - impl.RepositionSelectionHandles( xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT ); + impl.RepositionSelectionHandles(xPosition, yPosition, Controller::NoTextTap::HIGHLIGHT); } } void ControllerImplEventHandler::OnSelectAllEvent(Controller::Impl& impl) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false"); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false"); - if( impl.mEventData ) + if(impl.mEventData) { EventData& eventData = *impl.mEventData; - if( eventData.mSelectionEnabled ) + if(eventData.mSelectionEnabled && eventData.mState != EventData::INACTIVE) { - ModelPtr& model = impl.mModel; + ModelPtr& model = impl.mModel; const Vector2& scrollPosition = model->mScrollPosition; // Calculates the logical position from the start. - impl.RepositionSelectionHandles( 0.f - scrollPosition.x, - 0.f - scrollPosition.y, - Controller::NoTextTap::HIGHLIGHT ); + impl.RepositionSelectionHandles(0.f - scrollPosition.x, + 0.f - scrollPosition.y, + Controller::NoTextTap::HIGHLIGHT); + + uint32_t oldStart = eventData.mLeftSelectionPosition; + uint32_t oldEnd = eventData.mRightSelectionPosition; - eventData.mLeftSelectionPosition = 0u; + eventData.mLeftSelectionPosition = 0u; eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count(); + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } } } } void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false"); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled ? "true" : "false"); - if( impl.mEventData ) + if(impl.mEventData) { EventData& eventData = *impl.mEventData; - if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING) + if(eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING) { - eventData.mPrimaryCursorPosition = 0u; + uint32_t oldStart = eventData.mLeftSelectionPosition; + uint32_t oldEnd = eventData.mRightSelectionPosition; + eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition; - impl.ChangeState( EventData::INACTIVE ); - eventData.mUpdateCursorPosition = true; - eventData.mUpdateInputStyle = true; + impl.ChangeState(EventData::EDITING); + eventData.mUpdateCursorPosition = true; + eventData.mUpdateInputStyle = true; eventData.mScrollAfterUpdatePosition = true; + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } + } + } +} + +void ControllerImplEventHandler::OnSelectRangeEvent(Controller::Impl& impl, const Event& event) +{ + if(impl.mEventData && impl.mEventData->mSelectionEnabled && impl.mEventData->mState != EventData::INACTIVE) + { + ModelPtr& model = impl.mModel; + const Vector2& scrollPosition = model->mScrollPosition; + + // Calculate the selection index. + const uint32_t length = static_cast(model->mLogicalModel->mText.Count()); + const uint32_t start = std::min(event.p2.mUint, length); + const uint32_t end = std::min(event.p3.mUint, length); + + if(start != end) + { + uint32_t oldStart = impl.mEventData->mLeftSelectionPosition; + uint32_t oldEnd = impl.mEventData->mRightSelectionPosition; + + // Calculates the logical position from the x,y coords. + impl.RepositionSelectionHandles(0.f - scrollPosition.x, 0.f - scrollPosition.y, Controller::NoTextTap::HIGHLIGHT); + + impl.mEventData->mLeftSelectionPosition = start; + impl.mEventData->mRightSelectionPosition = end; + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end); + } } } } void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled) { - ModelPtr& model = impl.mModel; + ModelPtr& model = impl.mModel; const Vector2& scrollPosition = model->mScrollPosition; // Convert from decorator's coords to text's coords. @@ -485,83 +777,90 @@ void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const E const float yPosition = event.p3.mFloat - scrollPosition.y; // Need to calculate the handle's new position. - bool matchedCharacter = false; - const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( model->mVisualModel, - model->mLogicalModel, - impl.mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); + bool matchedCharacter = false; + const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex(model->mVisualModel, + model->mLogicalModel, + impl.mMetrics, + xPosition, + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter); EventData& eventData = *impl.mEventData; + uint32_t oldStart = eventData.mLeftSelectionPosition; + uint32_t oldEnd = eventData.mRightSelectionPosition; - if( Event::GRAB_HANDLE_EVENT == event.type ) + if(Event::GRAB_HANDLE_EVENT == event.type) { - impl.ChangeState ( EventData::GRAB_HANDLE_PANNING ); + impl.ChangeState(EventData::GRAB_HANDLE_PANNING); - if( handleNewPosition != eventData.mPrimaryCursorPosition ) + if(handleNewPosition != eventData.mPrimaryCursorPosition) { // Updates the cursor position if the handle's new position is different than the current one. eventData.mUpdateCursorPosition = true; // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth). eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - eventData.mPrimaryCursorPosition = handleNewPosition; + eventData.mPrimaryCursorPosition = handleNewPosition; } // 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. eventData.mDecoratorUpdated = isSmoothHandlePanEnabled; } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) + else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type) { - impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING ); + impl.ChangeState(EventData::SELECTION_HANDLE_PANNING); - if( ( handleNewPosition != eventData.mLeftSelectionPosition ) && - ( handleNewPosition != eventData.mRightSelectionPosition ) ) + if((handleNewPosition != eventData.mLeftSelectionPosition) && + (handleNewPosition != eventData.mRightSelectionPosition)) { // Updates the highlight box if the handle's new position is different than the current one. eventData.mUpdateHighlightBox = true; // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - eventData.mLeftSelectionPosition = handleNewPosition; + eventData.mLeftSelectionPosition = handleNewPosition; } // 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. eventData.mDecoratorUpdated = isSmoothHandlePanEnabled; // Will define the order to scroll the text to match the handle position. - eventData.mIsLeftHandleSelected = true; + eventData.mIsLeftHandleSelected = true; eventData.mIsRightHandleSelected = false; } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) + else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type) { - impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING ); + impl.ChangeState(EventData::SELECTION_HANDLE_PANNING); - if( ( handleNewPosition != eventData.mRightSelectionPosition ) && - ( handleNewPosition != eventData.mLeftSelectionPosition ) ) + if((handleNewPosition != eventData.mRightSelectionPosition) && + (handleNewPosition != eventData.mLeftSelectionPosition)) { // Updates the highlight box if the handle's new position is different than the current one. eventData.mUpdateHighlightBox = true; // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - eventData.mRightSelectionPosition = handleNewPosition; + eventData.mRightSelectionPosition = handleNewPosition; } // 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. eventData.mDecoratorUpdated = isSmoothHandlePanEnabled; // Will define the order to scroll the text to match the handle position. - eventData.mIsLeftHandleSelected = false; + eventData.mIsLeftHandleSelected = false; eventData.mIsRightHandleSelected = true; } + + if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition))) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } } void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling) { CharacterIndex handlePosition = 0u; - if( handleStopScrolling || isSmoothHandlePanEnabled ) + if(handleStopScrolling || isSmoothHandlePanEnabled) { - ModelPtr& model = impl.mModel; + ModelPtr& model = impl.mModel; const Vector2& scrollPosition = model->mScrollPosition; // Convert from decorator's coords to text's coords. @@ -569,97 +868,104 @@ void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const const float yPosition = event.p3.mFloat - scrollPosition.y; bool matchedCharacter = false; - handlePosition = Text::GetClosestCursorIndex( model->mVisualModel, - model->mLogicalModel, - impl.mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); + handlePosition = Text::GetClosestCursorIndex(model->mVisualModel, + model->mLogicalModel, + impl.mMetrics, + xPosition, + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter); } EventData& eventData = *impl.mEventData; + uint32_t oldStart = eventData.mLeftSelectionPosition; + uint32_t oldEnd = eventData.mRightSelectionPosition; - if( Event::GRAB_HANDLE_EVENT == event.type ) + if(Event::GRAB_HANDLE_EVENT == event.type) { - eventData.mUpdateCursorPosition = true; + eventData.mUpdateCursorPosition = true; eventData.mUpdateGrabHandlePosition = true; - eventData.mUpdateInputStyle = true; + eventData.mUpdateInputStyle = true; - if( !impl.IsClipboardEmpty() ) + if(!impl.IsClipboardEmpty()) { - impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup + impl.ChangeState(EventData::EDITING_WITH_PASTE_POPUP); // Moving grabhandle will show Paste Popup } - if( handleStopScrolling || isSmoothHandlePanEnabled ) + if(handleStopScrolling || isSmoothHandlePanEnabled) { eventData.mScrollAfterUpdatePosition = true; - eventData.mPrimaryCursorPosition = handlePosition; + eventData.mPrimaryCursorPosition = handlePosition; } } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) + else if(Event::LEFT_SELECTION_HANDLE_EVENT == event.type) { - impl.ChangeState( EventData::SELECTING ); + impl.ChangeState(EventData::SELECTING); - eventData.mUpdateHighlightBox = true; - eventData.mUpdateLeftSelectionPosition = true; + eventData.mUpdateHighlightBox = true; + eventData.mUpdateLeftSelectionPosition = true; eventData.mUpdateRightSelectionPosition = true; - if( handleStopScrolling || isSmoothHandlePanEnabled ) + if(handleStopScrolling || isSmoothHandlePanEnabled) { eventData.mScrollAfterUpdatePosition = true; - if( ( handlePosition != eventData.mRightSelectionPosition ) && - ( handlePosition != eventData.mLeftSelectionPosition ) ) + if((handlePosition != eventData.mRightSelectionPosition) && + (handlePosition != eventData.mLeftSelectionPosition)) { eventData.mLeftSelectionPosition = handlePosition; } } } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) + else if(Event::RIGHT_SELECTION_HANDLE_EVENT == event.type) { - impl.ChangeState( EventData::SELECTING ); + impl.ChangeState(EventData::SELECTING); - eventData.mUpdateHighlightBox = true; + eventData.mUpdateHighlightBox = true; eventData.mUpdateRightSelectionPosition = true; - eventData.mUpdateLeftSelectionPosition = true; + eventData.mUpdateLeftSelectionPosition = true; - if( handleStopScrolling || isSmoothHandlePanEnabled ) + if(handleStopScrolling || isSmoothHandlePanEnabled) { eventData.mScrollAfterUpdatePosition = true; - if( ( handlePosition != eventData.mRightSelectionPosition ) && - ( handlePosition != eventData.mLeftSelectionPosition ) ) + if((handlePosition != eventData.mRightSelectionPosition) && + (handlePosition != eventData.mLeftSelectionPosition)) { eventData.mRightSelectionPosition = handlePosition; } } } + if((impl.mSelectableControlInterface != nullptr) && ((oldStart != eventData.mLeftSelectionPosition) || (oldEnd != eventData.mRightSelectionPosition))) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } + eventData.mDecoratorUpdated = true; } void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled) { - ModelPtr& model = impl.mModel; - Vector2& scrollPosition = model->mScrollPosition; - VisualModelPtr& visualModel = model->mVisualModel; + ModelPtr& model = impl.mModel; + Vector2& scrollPosition = model->mScrollPosition; + VisualModelPtr& visualModel = model->mVisualModel; - const float xSpeed = event.p2.mFloat; - const float ySpeed = event.p3.mFloat; - const Vector2& layoutSize = visualModel->GetLayoutSize(); - const Vector2 currentScrollPosition = scrollPosition; + const float xSpeed = event.p2.mFloat; + const float ySpeed = event.p3.mFloat; + const Vector2& layoutSize = visualModel->GetLayoutSize(); + const Vector2 currentScrollPosition = scrollPosition; scrollPosition.x += xSpeed; scrollPosition.y += ySpeed; - impl.ClampHorizontalScroll( layoutSize ); - impl.ClampVerticalScroll( layoutSize ); + impl.ClampHorizontalScroll(layoutSize); + impl.ClampVerticalScroll(layoutSize); - EventData& eventData = *impl.mEventData; + EventData& eventData = *impl.mEventData; DecoratorPtr& decorator = eventData.mDecorator; bool endOfScroll = false; - if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) ) + if(Vector2::ZERO == (currentScrollPosition - scrollPosition)) { // Notify the decorator there is no more text to scroll. // The decorator won't send more scroll events. @@ -669,25 +975,25 @@ void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const } // Set the position of the handle. - const bool scrollRightDirection = xSpeed > 0.f; - const bool scrollBottomDirection = ySpeed > 0.f; - const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type; + const bool scrollRightDirection = xSpeed > 0.f; + const bool scrollBottomDirection = ySpeed > 0.f; + const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type; const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type; - if( Event::GRAB_HANDLE_EVENT == event.type ) + if(Event::GRAB_HANDLE_EVENT == event.type) { - impl.ChangeState( EventData::GRAB_HANDLE_PANNING ); + impl.ChangeState(EventData::GRAB_HANDLE_PANNING); // Get the grab handle position in decorator coords. - Vector2 position = decorator->GetPosition( GRAB_HANDLE ); + Vector2 position = decorator->GetPosition(GRAB_HANDLE); - if( decorator->IsHorizontalScrollEnabled() ) + if(decorator->IsHorizontalScrollEnabled()) { // Position the grag handle close to either the left or right edge. position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width; } - if( decorator->IsVerticalScrollEnabled() ) + if(decorator->IsVerticalScrollEnabled()) { position.x = eventData.mCursorHookPositionX; @@ -697,41 +1003,41 @@ void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const // Get the new handle position. // The grab handle's position is in decorator's coords. Need to transforms to text's coords. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel, - impl.mModel->mLogicalModel, - impl.mMetrics, - position.x - scrollPosition.x, - position.y - scrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( eventData.mPrimaryCursorPosition != handlePosition ) + bool matchedCharacter = false; + const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel, + impl.mModel->mLogicalModel, + impl.mMetrics, + position.x - scrollPosition.x, + position.y - scrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter); + + if(eventData.mPrimaryCursorPosition != handlePosition) { - eventData.mUpdateCursorPosition = true; - eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; + eventData.mUpdateCursorPosition = true; + eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; eventData.mScrollAfterUpdatePosition = true; - eventData.mPrimaryCursorPosition = handlePosition; + eventData.mPrimaryCursorPosition = handlePosition; } eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition; // Updates the decorator if the soft handle panning is enabled. eventData.mDecoratorUpdated = isSmoothHandlePanEnabled; } - else if( leftSelectionHandleEvent || rightSelectionHandleEvent ) + else if(leftSelectionHandleEvent || rightSelectionHandleEvent) { - impl.ChangeState( EventData::SELECTION_HANDLE_PANNING ); + impl.ChangeState(EventData::SELECTION_HANDLE_PANNING); // Get the selection handle position in decorator coords. - Vector2 position = decorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE ); + Vector2 position = decorator->GetPosition(leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE); - if( decorator->IsHorizontalScrollEnabled() ) + if(decorator->IsHorizontalScrollEnabled()) { // Position the selection handle close to either the left or right edge. position.x = scrollRightDirection ? 0.f : visualModel->mControlSize.width; } - if( decorator->IsVerticalScrollEnabled() ) + if(decorator->IsVerticalScrollEnabled()) { position.x = eventData.mCursorHookPositionX; @@ -741,44 +1047,51 @@ void ControllerImplEventHandler::OnHandleScrolling(Controller::Impl& impl, const // Get the new handle position. // The selection handle's position is in decorator's coords. Need to transform to text's coords. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( visualModel, - impl.mModel->mLogicalModel, - impl.mMetrics, - position.x - scrollPosition.x, - position.y - scrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( leftSelectionHandleEvent ) + bool matchedCharacter = false; + const CharacterIndex handlePosition = Text::GetClosestCursorIndex(visualModel, + impl.mModel->mLogicalModel, + impl.mMetrics, + position.x - scrollPosition.x, + position.y - scrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter); + uint32_t oldStart = eventData.mLeftSelectionPosition; + uint32_t oldEnd = eventData.mRightSelectionPosition; + + if(leftSelectionHandleEvent) { - const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition ); + const bool differentHandles = (eventData.mLeftSelectionPosition != handlePosition) && (eventData.mRightSelectionPosition != handlePosition); - if( differentHandles || endOfScroll ) + if(differentHandles || endOfScroll) { - eventData.mUpdateHighlightBox = true; - eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; + eventData.mUpdateHighlightBox = true; + eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled; - eventData.mLeftSelectionPosition = handlePosition; + eventData.mLeftSelectionPosition = handlePosition; } } else { - const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition ); - if( differentHandles || endOfScroll ) + const bool differentHandles = (eventData.mRightSelectionPosition != handlePosition) && (eventData.mLeftSelectionPosition != handlePosition); + if(differentHandles || endOfScroll) { - eventData.mUpdateHighlightBox = true; + eventData.mUpdateHighlightBox = true; eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; - eventData.mRightSelectionPosition = handlePosition; + eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; + eventData.mRightSelectionPosition = handlePosition; } } - if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition ) + if(eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition) { impl.RepositionSelectionHandles(); eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled; + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } } } eventData.mDecoratorUpdated = true;