${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
--- /dev/null
+/*
+ * 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 <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/key.h>
+#include <dali/integration-api/debug.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/cursor-helper-functions.h>
+
+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<GestureState>( 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
--- /dev/null
+#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 <dali-toolkit/internal/text/text-controller-impl.h>
+
+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
/*
- * 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.
#include <dali-toolkit/internal/text/text-controller-impl.h>
// EXTERNAL INCLUDES
-#include <dali/public-api/adaptor-framework/key.h>
#include <dali/public-api/rendering/renderer.h>
#include <dali/integration-api/debug.h>
#include <limits>
#include <dali-toolkit/internal/text/segmentation.h>
#include <dali-toolkit/internal/text/shaper.h>
#include <dali-toolkit/internal/text/text-control-interface.h>
+#include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
#include <dali-toolkit/internal/text/text-run-container.h>
using namespace Dali;
};
#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<float>::max();
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<GestureState>( 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)
#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.
//Forward declarations
struct CursorInfo;
struct FontDefaults;
+struct ControllerImplEventHandler;
class SelectableControlInterface;
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