From: Adeel Kazmi Date: Thu, 15 Oct 2020 08:57:27 +0000 (+0100) Subject: (Text Controller Impl) Moved event handling related methods into a separate struct X-Git-Tag: dali_1.9.35~1^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=f8abce594a03f5c11e363da5c13f54000496008a (Text Controller Impl) Moved event handling related methods into a separate struct Change-Id: I0e9f942f7dce110467dd283c08859f6c97e2b4e6 --- diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 0d6b781..72a5626 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -142,6 +142,7 @@ SET( toolkit_src_files ${toolkit_src_dir}/text/text-controller.cpp ${toolkit_src_dir}/text/text-controller-event-handler.cpp ${toolkit_src_dir}/text/text-controller-impl.cpp + ${toolkit_src_dir}/text/text-controller-impl-event-handler.cpp ${toolkit_src_dir}/text/text-controller-input-font-handler.cpp ${toolkit_src_dir}/text/text-controller-placeholder-handler.cpp ${toolkit_src_dir}/text/text-effects-style.cpp diff --git a/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp b/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp new file mode 100644 index 0000000..7b50a5c --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-impl-event-handler.cpp @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include + +using namespace Dali; + +namespace +{ + +#if defined(DEBUG_ENABLED) +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); +#endif + +} // namespace + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Text +{ + +void ControllerImplEventHandler::OnCursorKeyEvent(Controller::Impl& impl, const Event& event) +{ + 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; + + CharacterIndex& primaryCursorPosition = eventData.mPrimaryCursorPosition; + CharacterIndex previousPrimaryCursorPosition = primaryCursorPosition; + + if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) + { + if( primaryCursorPosition > 0u ) + { + if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() ) + { + primaryCursorPosition = std::min(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } + else + { + primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition - 1u ); + } + } + } + else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) + { + if( logicalModel->mText.Count() > primaryCursorPosition ) + { + if ( !isShiftModifier && eventData.mDecorator->IsHighlightVisible() ) + { + primaryCursorPosition = std::max(eventData.mLeftSelectionPosition, eventData.mRightSelectionPosition); + } + else + { + primaryCursorPosition = impl.CalculateNewCursorIndex( primaryCursorPosition ); + } + } + } + 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 ) + { + characterIndex = primaryCursorPosition - 1u; + } + + 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 ); + + // Get the line above. + const LineRun& line = *( visualModel->mLines.Begin() + previousLineIndex ); + + // Get the next hit 'y' point. + const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); + + // 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 ); + } + 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 ) + { + characterIndex = primaryCursorPosition - 1u; + } + + const LineIndex lineIndex = visualModel->GetLineOfCharacter( characterIndex ); + + if( lineIndex + 1u < visualModel->mLines.Count() ) + { + // Retrieve the cursor position info. + CursorInfo cursorInfo; + impl.GetCursorPosition( primaryCursorPosition, cursorInfo ); + + // Get the line below. + 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 ); + + // 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 ); + } + } + + if ( !isShiftModifier && eventData.mState != EventData::SELECTING ) + { + // Update selection position after moving the cursor + eventData.mLeftSelectionPosition = primaryCursorPosition; + eventData.mRightSelectionPosition = primaryCursorPosition; + } + + 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 ) + { + // Shift-Left/Right to select the text + int cursorPositionDelta = primaryCursorPosition - previousPrimaryCursorPosition; + if ( cursorPositionDelta > 0 || eventData.mRightSelectionPosition > 0u ) // Check the boundary + { + eventData.mRightSelectionPosition += cursorPositionDelta; + } + selecting = true; + } + 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 ) + { + // Notify the cursor position to the InputMethodContext. + if( eventData.mInputMethodContext ) + { + eventData.mInputMethodContext.SetCursorPosition( primaryCursorPosition ); + eventData.mInputMethodContext.NotifyCursorPosition(); + } + + impl.ChangeState( EventData::SELECTING ); + + eventData.mUpdateLeftSelectionPosition = true; + eventData.mUpdateRightSelectionPosition = 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 ) + { + eventData.mDecorator->SetPopupActive( false ); + } + } + } + else + { + // Handle normal cursor move + impl.ChangeState( EventData::EDITING ); + eventData.mUpdateCursorPosition = true; + } + + eventData.mUpdateInputStyle = true; + eventData.mScrollAfterUpdatePosition = true; +} + +void ControllerImplEventHandler::OnTapEvent(Controller::Impl& impl, const Event& event) +{ + 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; + + if( 1u == tapCount ) + { + 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; + + // 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 ); + + // When the cursor position is changing, delay cursor blinking + eventData.mDecorator->DelayCursorBlink(); + } + else + { + eventData.mPrimaryCursorPosition = 0u; + } + + // Update selection position after tapping + eventData.mLeftSelectionPosition = eventData.mPrimaryCursorPosition; + eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition; + + eventData.mUpdateCursorPosition = true; + eventData.mUpdateGrabHandlePosition = true; + eventData.mScrollAfterUpdatePosition = true; + eventData.mUpdateInputStyle = true; + + // Notify the cursor position to the InputMethodContext. + if( eventData.mInputMethodContext ) + { + eventData.mInputMethodContext.SetCursorPosition( eventData.mPrimaryCursorPosition ); + eventData.mInputMethodContext.NotifyCursorPosition(); + } + } + else if( 2u == tapCount ) + { + 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 ); + } + } + } +} + +void ControllerImplEventHandler::OnPanEvent(Controller::Impl& impl, const Event& event) +{ + if( impl.mEventData ) + { + EventData& eventData = *impl.mEventData; + DecoratorPtr& decorator = eventData.mDecorator; + + const bool isHorizontalScrollEnabled = decorator->IsHorizontalScrollEnabled(); + const bool isVerticalScrollEnabled = decorator->IsVerticalScrollEnabled(); + + if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled ) + { + // Nothing to do if scrolling is not enabled. + return; + } + + 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 ); + break; + } + case GestureState::CONTINUING: + { + ModelPtr& model = impl.mModel; + + const Vector2& layoutSize = model->mVisualModel->GetLayoutSize(); + Vector2& scrollPosition = model->mScrollPosition; + const Vector2 currentScroll = scrollPosition; + + if( isHorizontalScrollEnabled ) + { + const float displacementX = event.p2.mFloat; + scrollPosition.x += displacementX; + + impl.ClampHorizontalScroll( layoutSize ); + } + + if( isVerticalScrollEnabled ) + { + const float displacementY = event.p3.mFloat; + scrollPosition.y += displacementY; + + impl.ClampVerticalScroll( layoutSize ); + } + + 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 ); + break; + } + default: + break; + } + } +} + +void ControllerImplEventHandler::OnLongPressEvent(Controller::Impl& impl, const Event& event) +{ + DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); + + if( impl.mEventData ) + { + EventData& eventData = *impl.mEventData; + + if( !impl.IsShowingRealText() && ( EventData::EDITING == eventData.mState ) ) + { + impl.ChangeState( EventData::EDITING_WITH_POPUP ); + eventData.mDecoratorUpdated = true; + eventData.mUpdateInputStyle = true; + } + else + { + if( eventData.mSelectionEnabled ) + { + ModelPtr& model = impl.mModel; + + // 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.mLongPressAction ); + } + } + } +} + +void ControllerImplEventHandler::OnHandleEvent(Controller::Impl& impl, const Event& event) +{ + if( impl.mEventData ) + { + 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 ) + { + OnHandlePressed(impl, event, isSmoothHandlePanEnabled); + } // end ( HANDLE_PRESSED == state ) + else if( ( HANDLE_RELEASED == state ) || + handleStopScrolling ) + { + OnHandleReleased(impl, event, isSmoothHandlePanEnabled, handleStopScrolling); + } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) ) + else if( HANDLE_SCROLLING == state ) + { + OnHandleScrolling(impl, event, isSmoothHandlePanEnabled); + } // end ( HANDLE_SCROLLING == state ) + } +} + +void ControllerImplEventHandler::OnSelectEvent(Controller::Impl& impl, const Event& event ) +{ + if( impl.mEventData && impl.mEventData->mSelectionEnabled ) + { + ModelPtr& model = impl.mModel; + const Vector2& scrollPosition = model->mScrollPosition; + + // Convert from control's coords to text's coords. + const float xPosition = event.p2.mFloat - scrollPosition.x; + const float yPosition = event.p3.mFloat - scrollPosition.y; + + // Calculates the logical position from the x,y coords. + 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"); + + if( impl.mEventData ) + { + EventData& eventData = *impl.mEventData; + if( eventData.mSelectionEnabled ) + { + 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 ); + + eventData.mLeftSelectionPosition = 0u; + eventData.mRightSelectionPosition = model->mLogicalModel->mText.Count(); + } + } +} + +void ControllerImplEventHandler::OnSelectNoneEvent(Controller::Impl& impl) +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", impl.mEventData->mSelectionEnabled?"true":"false"); + + if( impl.mEventData ) + { + EventData& eventData = *impl.mEventData; + if( eventData.mSelectionEnabled && eventData.mState == EventData::SELECTING) + { + eventData.mPrimaryCursorPosition = 0u; + eventData.mLeftSelectionPosition = eventData.mRightSelectionPosition = eventData.mPrimaryCursorPosition; + impl.ChangeState( EventData::INACTIVE ); + eventData.mUpdateCursorPosition = true; + eventData.mUpdateInputStyle = true; + eventData.mScrollAfterUpdatePosition = true; + } + } +} + +void ControllerImplEventHandler::OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled) +{ + ModelPtr& model = impl.mModel; + const Vector2& scrollPosition = model->mScrollPosition; + + // Convert from decorator's coords to text's coords. + const float xPosition = event.p2.mFloat - scrollPosition.x; + 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 ); + + EventData& eventData = *impl.mEventData; + + if( Event::GRAB_HANDLE_EVENT == event.type ) + { + impl.ChangeState ( EventData::GRAB_HANDLE_PANNING ); + + 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; + } + + // 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 ) + { + impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING ); + + 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; + } + + // 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.mIsRightHandleSelected = false; + } + else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) + { + impl.ChangeState ( EventData::SELECTION_HANDLE_PANNING ); + + 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; + } + + // 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.mIsRightHandleSelected = true; + } +} + +void ControllerImplEventHandler::OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling) +{ + CharacterIndex handlePosition = 0u; + if( handleStopScrolling || isSmoothHandlePanEnabled ) + { + ModelPtr& model = impl.mModel; + const Vector2& scrollPosition = model->mScrollPosition; + + // Convert from decorator's coords to text's coords. + const float xPosition = event.p2.mFloat - scrollPosition.x; + 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 ); + } + + EventData& eventData = *impl.mEventData; + + if( Event::GRAB_HANDLE_EVENT == event.type ) + { + eventData.mUpdateCursorPosition = true; + eventData.mUpdateGrabHandlePosition = true; + eventData.mUpdateInputStyle = true; + + if( !impl.IsClipboardEmpty() ) + { + impl.ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup + } + + if( handleStopScrolling || isSmoothHandlePanEnabled ) + { + eventData.mScrollAfterUpdatePosition = true; + eventData.mPrimaryCursorPosition = handlePosition; + } + } + else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) + { + impl.ChangeState( EventData::SELECTING ); + + eventData.mUpdateHighlightBox = true; + eventData.mUpdateLeftSelectionPosition = true; + eventData.mUpdateRightSelectionPosition = true; + + if( handleStopScrolling || isSmoothHandlePanEnabled ) + { + eventData.mScrollAfterUpdatePosition = true; + + if( ( handlePosition != eventData.mRightSelectionPosition ) && + ( handlePosition != eventData.mLeftSelectionPosition ) ) + { + eventData.mLeftSelectionPosition = handlePosition; + } + } + } + else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) + { + impl.ChangeState( EventData::SELECTING ); + + eventData.mUpdateHighlightBox = true; + eventData.mUpdateRightSelectionPosition = true; + eventData.mUpdateLeftSelectionPosition = true; + + if( handleStopScrolling || isSmoothHandlePanEnabled ) + { + eventData.mScrollAfterUpdatePosition = true; + if( ( handlePosition != eventData.mRightSelectionPosition ) && + ( handlePosition != eventData.mLeftSelectionPosition ) ) + { + eventData.mRightSelectionPosition = handlePosition; + } + } + } + + 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; + + 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 ); + + EventData& eventData = *impl.mEventData; + DecoratorPtr& decorator = eventData.mDecorator; + + bool endOfScroll = false; + if( Vector2::ZERO == ( currentScrollPosition - scrollPosition ) ) + { + // Notify the decorator there is no more text to scroll. + // The decorator won't send more scroll events. + decorator->NotifyEndOfScroll(); + // Still need to set the position of the handle. + endOfScroll = true; + } + + // 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 rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type; + + if( Event::GRAB_HANDLE_EVENT == event.type ) + { + impl.ChangeState( EventData::GRAB_HANDLE_PANNING ); + + // Get the grab handle position in decorator coords. + Vector2 position = decorator->GetPosition( GRAB_HANDLE ); + + 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() ) + { + position.x = eventData.mCursorHookPositionX; + + // Position the grag handle close to either the top or bottom edge. + position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height; + } + + // 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 ) + { + eventData.mUpdateCursorPosition = true; + eventData.mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; + eventData.mScrollAfterUpdatePosition = true; + eventData.mPrimaryCursorPosition = handlePosition; + } + eventData.mUpdateInputStyle = eventData.mUpdateCursorPosition; + + // Updates the decorator if the soft handle panning is enabled. + eventData.mDecoratorUpdated = isSmoothHandlePanEnabled; + } + else if( leftSelectionHandleEvent || rightSelectionHandleEvent ) + { + 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 ); + + 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() ) + { + position.x = eventData.mCursorHookPositionX; + + // Position the grag handle close to either the top or bottom edge. + position.y = scrollBottomDirection ? 0.f : visualModel->mControlSize.height; + } + + // 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 ) + { + const bool differentHandles = ( eventData.mLeftSelectionPosition != handlePosition ) && ( eventData.mRightSelectionPosition != handlePosition ); + + if( differentHandles || endOfScroll ) + { + eventData.mUpdateHighlightBox = true; + eventData.mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; + eventData.mUpdateRightSelectionPosition = isSmoothHandlePanEnabled; + eventData.mLeftSelectionPosition = handlePosition; + } + } + else + { + const bool differentHandles = ( eventData.mRightSelectionPosition != handlePosition ) && ( eventData.mLeftSelectionPosition != handlePosition ); + if( differentHandles || endOfScroll ) + { + eventData.mUpdateHighlightBox = true; + eventData.mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; + eventData.mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; + eventData.mRightSelectionPosition = handlePosition; + } + } + + if( eventData.mUpdateLeftSelectionPosition || eventData.mUpdateRightSelectionPosition ) + { + impl.RepositionSelectionHandles(); + + eventData.mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled; + } + } + eventData.mDecoratorUpdated = true; +} + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/text/text-controller-impl-event-handler.h b/dali-toolkit/internal/text/text-controller-impl-event-handler.h new file mode 100644 index 0000000..52ac94a --- /dev/null +++ b/dali-toolkit/internal/text/text-controller-impl-event-handler.h @@ -0,0 +1,139 @@ +#ifndef DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_H +#define DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_H + +/* + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Text +{ + +/** + * Contains all the event handling methods for Text::Controller::Impl + */ +struct ControllerImplEventHandler +{ + /** + * @brief Called by Controller::Impl when a cursor key event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnCursorKeyEvent(Controller::Impl& controllerImpl, const Event& event); + + /** + * @brief Called by Controller::Impl when a tap event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnTapEvent(Controller::Impl& controllerImpl, const Event& event); + + /** + * @brief Called by Controller::Impl when a pan event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnPanEvent(Controller::Impl& controllerImpl, const Event& event); + + /** + * @brief Called by Controller::Impl when a long press event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnLongPressEvent(Controller::Impl& controllerImpl, const Event& event); + + /** + * @brief Called by Controller::Impl when a handle event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnHandleEvent(Controller::Impl& controllerImpl, const Event& event); + + /** + * @brief Called by Controller::Impl when a select event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnSelectEvent(Controller::Impl& controllerImpl, const Event& event ); + + /** + * @brief Called by Controller::Impl when a select all event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnSelectAllEvent(Controller::Impl& controllerImpl); + + /** + * @brief Called by Controller::Impl when a select none event is received. + * + * @param controllerImpl A reference to Controller::Impl + * @param event The event + */ + static void OnSelectNoneEvent(Controller::Impl& controllerImpl); + +private: + + /** + * @brief Called by OnHandleEvent when we are in the Pressed state. + * + * @param impl A reference to Controller::Impl + * @param event The event + * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled + */ + static void OnHandlePressed(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled); + + /** + * @brief Called by OnHandleEvent when we are in the Released state. + * + * @param impl A reference to Controller::Impl + * @param event The event + * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled + * @param handleStopScrolling Whether we should handle stop scrolling or not + */ + static void OnHandleReleased(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled, const bool handleStopScrolling); + + /** + * @brief Called by OnHandleEvent when we are in the Scrolling state. + * + * @param impl A reference to Controller::Impl + * @param event The event + * @param isSmoothHandlePanEnabled Whether smooth handle pan is enabled + */ + static void OnHandleScrolling(Controller::Impl& impl, const Event& event, const bool isSmoothHandlePanEnabled); +}; + +} // namespace Text + +} // namespace Toolkit + +} // namespace Dali + +#endif // DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_EVENT_HANDLER_H diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index 719f220..0cd6adb 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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,7 +19,6 @@ #include // EXTERNAL INCLUDES -#include #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #include using namespace Dali; @@ -53,7 +53,7 @@ struct SelectionBoxInfo }; #if defined(DEBUG_ENABLED) - Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif const float MAX_FLOAT = std::numeric_limits::max(); @@ -1341,704 +1341,42 @@ float Controller::Impl::GetDefaultFontLineHeight() void Controller::Impl::OnCursorKeyEvent( const Event& event ) { - if( NULL == mEventData || !IsShowingRealText() ) - { - // Nothing to do if there is no text input. - return; - } - - int keyCode = event.p1.mInt; - bool isShiftModifier = event.p2.mBool; - - CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition; - - if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) - { - if( mEventData->mPrimaryCursorPosition > 0u ) - { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u ); - } - } - } - else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) - { - if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition ) - { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition ); - } - } - } - 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( mEventData->mPrimaryCursorPosition > 0u ) - { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; - } - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); - - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // Get the line above. - const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex ); - - // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); - - // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); - } - 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( mEventData->mPrimaryCursorPosition > 0u ) - { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; - } - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - - if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() ) - { - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // Get the line below. - const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u ); - - // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender ); - - // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); - } - } - - if ( !isShiftModifier && mEventData->mState != EventData::SELECTING ) - { - // Update selection position after moving the cursor - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - } - - if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag ) - { - // Handle text selection - bool selecting = false; - - if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) - { - // Shift-Left/Right to select the text - int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition; - if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary - { - mEventData->mRightSelectionPosition += cursorPositionDelta; - } - selecting = true; - } - else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) - { - // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text - selecting = true; - } - - if ( selecting ) - { - // Notify the cursor position to the InputMethodContext. - if( mEventData->mInputMethodContext ) - { - mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); - mEventData->mInputMethodContext.NotifyCursorPosition(); - } - - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mUpdateHighlightBox = true; - - // Hide the text selection popup if select the text using keyboard instead of moving grab handles - if( mEventData->mGrabHandlePopupEnabled ) - { - mEventData->mDecorator->SetPopupActive( false ); - } - } - } - else - { - // Handle normal cursor move - ChangeState( EventData::EDITING ); - mEventData->mUpdateCursorPosition = true; - } - - mEventData->mUpdateInputStyle = true; - mEventData->mScrollAfterUpdatePosition = true; + ControllerImplEventHandler::OnCursorKeyEvent(*this, event); } void Controller::Impl::OnTapEvent( const Event& event ) { - if( NULL != mEventData ) - { - const unsigned int tapCount = event.p1.mUint; - - if( 1u == tapCount ) - { - if( IsShowingRealText() ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Keep the tap 'x' position. Used to move the cursor. - mEventData->mCursorHookPositionX = xPosition; - - // Whether to touch point hits on a glyph. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::TAP, - matchedCharacter ); - - // When the cursor position is changing, delay cursor blinking - mEventData->mDecorator->DelayCursorBlink(); - } - else - { - mEventData->mPrimaryCursorPosition = 0u; - } - - // Update selection position after tapping - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; - - // Notify the cursor position to the InputMethodContext. - if( mEventData->mInputMethodContext ) - { - mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); - mEventData->mInputMethodContext.NotifyCursorPosition(); - } - } - else if( 2u == tapCount ) - { - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - mEventData->mDoubleTapAction ); - } - } - } + ControllerImplEventHandler::OnTapEvent(*this, event); } void Controller::Impl::OnPanEvent( const Event& event ) { - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - return; - } - - const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled(); - const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled(); - - if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled ) - { - // Nothing to do if scrolling is not enabled. - return; - } - - const GestureState state = static_cast( event.p1.mInt ); - switch( state ) - { - case GestureState::STARTED: - { - // Will remove the cursor, handles or text's popup, ... - ChangeState( EventData::TEXT_PANNING ); - break; - } - case GestureState::CONTINUING: - { - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mModel->mScrollPosition; - - if( isHorizontalScrollEnabled ) - { - const float displacementX = event.p2.mFloat; - mModel->mScrollPosition.x += displacementX; - - ClampHorizontalScroll( layoutSize ); - } - - if( isVerticalScrollEnabled ) - { - const float displacementY = event.p3.mFloat; - mModel->mScrollPosition.y += displacementY; - - ClampVerticalScroll( layoutSize ); - } - - mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - 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, ... - ChangeState( mEventData->mPreviousState ); - break; - } - default: - break; - } + ControllerImplEventHandler::OnPanEvent(*this, event); } void Controller::Impl::OnLongPressEvent( const Event& event ) { - DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); - - if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) ) - { - ChangeState( EventData::EDITING_WITH_POPUP ); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateInputStyle = true; - } - else - { - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - mEventData->mLongPressAction ); - } - } + ControllerImplEventHandler::OnLongPressEvent(*this, event); } void Controller::Impl::OnHandleEvent( const Event& event ) { - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - return; - } - - const unsigned int state = event.p1.mUint; - const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state ); - const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled(); - - if( HANDLE_PRESSED == state ) - { - // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Need to calculate the handle's new position. - bool matchedCharacter = false; - const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::GRAB_HANDLE_PANNING ); - - if( handleNewPosition != mEventData->mPrimaryCursorPosition ) - { - // Updates the cursor position if the handle's new position is different than the current one. - mEventData->mUpdateCursorPosition = true; - // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->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. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) && - ( handleNewPosition != mEventData->mRightSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->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. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // Will define the order to scroll the text to match the handle position. - mEventData->mIsLeftHandleSelected = true; - mEventData->mIsRightHandleSelected = false; - } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mRightSelectionPosition ) && - ( handleNewPosition != mEventData->mLeftSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->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. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // Will define the order to scroll the text to match the handle position. - mEventData->mIsLeftHandleSelected = false; - mEventData->mIsRightHandleSelected = true; - } - } // end ( HANDLE_PRESSED == state ) - else if( ( HANDLE_RELEASED == state ) || - handleStopScrolling ) - { - CharacterIndex handlePosition = 0u; - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - bool matchedCharacter = false; - handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); - } - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mUpdateInputStyle = true; - - if( !IsClipboardEmpty() ) - { - ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup - } - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mLeftSelectionPosition = handlePosition; - } - } - } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateLeftSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mRightSelectionPosition = handlePosition; - } - } - } - - mEventData->mDecoratorUpdated = true; - } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) ) - else if( HANDLE_SCROLLING == state ) - { - const float xSpeed = event.p2.mFloat; - const float ySpeed = event.p3.mFloat; - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScrollPosition = mModel->mScrollPosition; - - mModel->mScrollPosition.x += xSpeed; - mModel->mScrollPosition.y += ySpeed; - - ClampHorizontalScroll( layoutSize ); - ClampVerticalScroll( layoutSize ); - - bool endOfScroll = false; - if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) ) - { - // Notify the decorator there is no more text to scroll. - // The decorator won't send more scroll events. - mEventData->mDecorator->NotifyEndOfScroll(); - // Still need to set the position of the handle. - endOfScroll = true; - } - - // 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 rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type; - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::GRAB_HANDLE_PANNING ); - - // Get the grab handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the grag handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; - } - - // 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( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( mEventData->mPrimaryCursorPosition != handlePosition ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition; - - // Updates the decorator if the soft handle panning is enabled. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( leftSelectionHandleEvent || rightSelectionHandleEvent ) - { - ChangeState( EventData::SELECTION_HANDLE_PANNING ); - - // Get the selection handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the selection handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; - } - - // 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( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( leftSelectionHandleEvent ) - { - const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition ); - - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mLeftSelectionPosition = handlePosition; - } - } - else - { - const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition ); - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mRightSelectionPosition = handlePosition; - } - } - - if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) - { - RepositionSelectionHandles(); - - mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled; - } - } - mEventData->mDecoratorUpdated = true; - } // end ( HANDLE_SCROLLING == state ) + ControllerImplEventHandler::OnHandleEvent(*this, event); } void Controller::Impl::OnSelectEvent( const Event& event ) { - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled ) - { - // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - // Calculates the logical position from the x,y coords. - RepositionSelectionHandles( xPosition, - yPosition, - Controller::NoTextTap::HIGHLIGHT ); - } + ControllerImplEventHandler::OnSelectEvent(*this, event); } void Controller::Impl::OnSelectAllEvent() { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); - - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled ) - { - // Calculates the logical position from the start. - RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x, - 0.f - mModel->mScrollPosition.y, - Controller::NoTextTap::HIGHLIGHT ); - - mEventData->mLeftSelectionPosition = 0u; - mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count(); - } + ControllerImplEventHandler::OnSelectAllEvent(*this); } void Controller::Impl::OnSelectNoneEvent() { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); - - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING) - { - mEventData->mPrimaryCursorPosition = 0u; - mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; - ChangeState( EventData::INACTIVE ); - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateInputStyle = true; - mEventData->mScrollAfterUpdatePosition = true; - } + ControllerImplEventHandler::OnSelectNoneEvent(*this); } void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd) diff --git a/dali-toolkit/internal/text/text-controller-impl.h b/dali-toolkit/internal/text/text-controller-impl.h index 3fc33b3..fc30d7b 100755 --- a/dali-toolkit/internal/text/text-controller-impl.h +++ b/dali-toolkit/internal/text/text-controller-impl.h @@ -2,7 +2,7 @@ #define DALI_TOOLKIT_TEXT_CONTROLLER_IMPL_H /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 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. @@ -47,6 +47,7 @@ const float DEFAULT_TEXTFIT_STEP = 1.f; //Forward declarations struct CursorInfo; struct FontDefaults; +struct ControllerImplEventHandler; class SelectableControlInterface; @@ -823,6 +824,9 @@ public: float mTextFitMaxSize; ///< Maximum Font Size for text fit. Default 100 float mTextFitStepSize; ///< Step Size for font intervalse. Default 1 bool mTextFitEnabled : 1; ///< Whether the text's fit is enabled. + +private: + friend ControllerImplEventHandler; }; } // namespace Text